﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "DrawNewLevelAction.h"

#include "IDocument.h"
#include "ILevel.h"
#include "ActionDrawStraightElementInput.h"
#include "Vector3dUtils.h"
#include "Vector2dUtils.h"
#include "LevelUtils.h"
#include "ILevelHeaderBehavior.h"
#include "GbmpModelViewUtil.h"
#include "IGbmpPlanView.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "IElementBasicInformation.h"

#include "IUiView.h"
#include "IPresetParameterManager.h"
#include "GbmpPresetParameterUIUtils.h"
#include "IModelView.h"
#include "ICheckBoxGroupDefinition.h"
#include "IActionInputDialog.h"
#include "IActionInputEvent.h"
#include "IActionInputEventArgs.h"
#include "UiCommonDialog.h"
#include "IGenericElement.h"
#include "IElementStatus.h"

#include "StructureInstancePresetParameterUtils.h"
#include "IUserTransaction.h"
#include "GUiBaseInterfaceEnums.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    const static std::wstring s_gmWhetherCreateViewGroup = GBMP_TR(L"gmWhetherCreateViewGroup");
    const static std::wstring s_gmCheckCreatePlanView = GBMP_TR(L"gmCheckCreatePlanView");
    const static std::wstring s_gmCheckCreateCeilingView = GBMP_TR(L"gmCheckCreateCeilingView");
}

namespace
{
    LevelName GetLevelName(IDocument* pDoc)
    {
        return LevelUtils::GetNextLevelName(pDoc, ElementId::InvalidID);
    }

    std::wstring GetRawLevelName(IDocument* pDoc)
    {
        LevelName name = GetLevelName(pDoc);
        return name.Prefix + StringUtil::ToWString(name.Number) + name.Suffix;
    }

    std::vector<OwnerPtr<IParameter>> GetLevelHeaderBehaviorParameters(IGenericElement* pGenericElement)
    {
        std::vector<OwnerPtr<IParameter>> headerParams;
        DBG_WARN_AND_RETURN_UNLESS(pGenericElement, headerParams, L"输入参数为空",L"GDMPLab",L"2024-03-30");

        const ILevel* pLevel = quick_cast<const ILevel>(pGenericElement->GetExternalObject());
        DBG_WARN_AND_RETURN_UNLESS(pLevel, headerParams, L"pLevel为空",L"GDMPLab",L"2024-03-30");

        const ILevelHeaderBehavior* pHeaderBehavior = pLevel->GetLevelHeaderBehavior();
        if (pHeaderBehavior)
        {
            std::vector<int> parameterDefIds;
            pHeaderBehavior->ReportParameterDefinitions(&parameterDefIds);
            FOR_EACH(defId, parameterDefIds)
            {
                headerParams.push_back(pHeaderBehavior->GetNativeParameter(defId));
            }
        }

        return headerParams;
    }

    void UpdateLevelHeaderBehaviorParameters(ILevel* pLevel, const std::vector<OwnerPtr<IParameter>>& parameters)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pLevel, L"输入参数为空",L"GDMPLab",L"2024-03-30");

        ILevelHeaderBehavior* pNewHeaderBehavior = pLevel->GetLevelHeaderBehavior();
        if (pNewHeaderBehavior)
        {
            FOR_EACH(headerParam, parameters)
            {
                pNewHeaderBehavior->SetNativeParameter(headerParam.get());
            }
        }
    }
}

DrawNewLevelAction::DrawNewLevelAction(const gcmp::ElementId typeId)
    : m_isUserCancelled(false)
    , m_createAssociatedPlanView(true)
    , m_createAssociatedCeilingView(true)
    , m_typeId(typeId)
    , m_pInputDialog(nullptr)

{
    m_actionStatus = (int)DrawLineStatus::Not_Started;
    SetPromptMessage(GBMP_TR(L"绘制标高线 - 请输入标高线起始点"));
    m_pLevelLine = nullptr;
}

DrawNewLevelAction::~DrawNewLevelAction(void)
{
    GbmpPresetParameterUIUtils::UpdatePropertyPanel(ElementId::InvalidID);
    GbmpPresetParameterUIUtils::EnablePropertyPanel(true);

    if (m_pInputDialog && m_pInputDialog->GetInputEvent())
    {
        m_pInputDialog->GetInputEvent()->Delete(this);
    }
    m_pInputDialog = nullptr;
}

void DrawNewLevelAction::OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam)
{
    if (m_actionStatus == (int)DrawLineStatus::Getting_StartPoint)
    {
        if (m_isUserCancelled)
        {
            // 如果用户在画第一个点时就选择退出，那么就退出整个画线工具
            RemoveTempLevel();
            MarkFinishStatus(ActionFinishStatus::Successful);
            return;
        }

        SetPromptMessage(GBMP_TR(L"绘制标高线 - 请输入标高线终止点"));
        m_actionStatus = (int)DrawLineStatus::Getting_EndPoint;
        StartChildAction(IPickPointAction::Create(
            PickPointExchangeData(&m_endPt, &m_isUserCancelled, &m_pickedPoints)
            , IPickPointAction::CreateMoveCallback(&DrawNewLevelAction::DrawTempLevelLine, this)));

        // 确保预设参数被应用
        IPresetParameterManager* pPresetMgr = IPresetParameterManager::Get(GetDoc());
        DBG_WARN_AND_RETURN_VOID_UNLESS(pPresetMgr, L"pPresetMgr为空",L"GDMPLab",L"2024-03-30");
        bool bOk = pPresetMgr->GetPresetType(BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION, 0, m_typeId);
        DBG_WARN_AND_RETURN_VOID_UNLESS(bOk, L"GetPresetType失败",L"GDMPLab",L"2024-03-30");

        // 更新属性面板
        GbmpPresetParameterUIUtils::UpdatePropertyPanel(GetPreSetElementId());

        // 禁用预设参数设置
        GbmpPresetParameterUIUtils::EnablePropertyPanel(false);
    }
    else if (m_actionStatus == (int)DrawLineStatus::Getting_EndPoint)
    {
        if (m_isUserCancelled)
        {
            // 如果用户在画第二个点时选择退出，那么先退到画第一个点的状态
            m_actionStatus = (int)DrawLineStatus::Getting_StartPoint;
            m_isUserCancelled = false;
            StartChildAction(IPickPointAction::Create(
                PickPointExchangeData(&m_startPt, &m_isUserCancelled, &m_pickedPoints),
                nullptr));

            RemoveTempLevel();
            UpdateView();
            // 启用预设参数设置
            GbmpPresetParameterUIUtils::EnablePropertyPanel(true);
            // 更新属性面板
            GbmpPresetParameterUIUtils::UpdatePropertyPanel(GetPreSetElementId());

            return;
        }

        IDocument* pDoc = GetDoc();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

        //最终点的Z坐标都相同
        m_endPt.Set(m_endPt.X(), m_endPt.Y(), m_startPt.Z());

        if (Vector3dUtils::IsEqual(m_startPt, m_endPt))
        {
            UiCommonDialog::ShowMessageBox(GBMP_TR(L"绘制标高线"), GBMP_TR(L"起始点和结束点不能相同"), (int)UiCommonDialog::ButtonType::OK);
            SetPromptMessage(L"绘制标高线 - 请输入标高线终止点");
            m_pickedPoints.pop_back();
            m_actionStatus = (int)DrawLineStatus::Getting_EndPoint;
            OwnerPtr<IPickPointAction> endPickAction = IPickPointAction::Create(
                PickPointExchangeData(&m_endPt, &m_isUserCancelled, &m_pickedPoints)
                , IPickPointAction::CreateMoveCallback(&DrawNewLevelAction::DrawTempLevelLine, this));

            StartChildAction(TransferOwnership(endPickAction));
            return;
        }

        OwnerPtr<IUserTransaction> opUt = IUserTransaction::Create(pDoc, GBMP_TR(L"画标高线"));

        IGenericElement* pGenericElement = quick_cast<IGenericElement>(GetDoc()->GetElement(GetPreSetElementId()));
        DBG_WARN_AND_RETURN_VOID_UNLESS(pGenericElement, L"pGenericElement为空",L"GDMPLab",L"2024-03-30");

        // 获取预设参数的值（名字和标头类型）
        std::wstring levelNmae = pGenericElement->GetBasicInformation()->GetName();
        std::vector<OwnerPtr<IParameter>> headerParams = GetLevelHeaderBehaviorParameters(pGenericElement);
        
        //先删除临时标高（避免命名重名)
        RemoveTempLevel();

        gcmp::IModelView* pModelView = pCurrentView->GetModelView();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

        ILevel* pLevel = ILevel::Create(pDoc, m_typeId, levelNmae, m_startPt, m_endPt, pModelView->GetViewDirection(), ElementId::InvalidID, ElementCreationOptions::Normal);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pLevel, L"level为空",L"GDMPLab",L"2024-03-30");

        // 更新预设参数的值（标头类型）
        UpdateLevelHeaderBehaviorParameters(pLevel, headerParams);

        IElementBasicInformation *pBasicInformation = pLevel->GetOwnerElement()->GetBasicInformation();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pBasicInformation, L"无效pBasicInformation",L"GDMPLab",L"2024-03-30");

        //创建关联平面视图
        //if (m_createAssociatedCeilingView)
        //{
        //    GbmpModelViewUtil::CreateCeilingView(pDoc, pBasicInformation->GetName(), pLevel);
        //}

        if (m_createAssociatedPlanView)
        {
            GbmpPlanViewInput input;
            input.Document = pDoc;
            input.Name = pBasicInformation->GetName();
            input.Level = pLevel;
            IGbmpPlanView::Create(input);
        }

        pGenericElement->GetBasicInformation()->SetName(GetRawLevelName(pDoc));

        UpdateView();
        SetPromptMessage(GBMP_TR(L"绘制标高线 - 请输入标高线起始点"));
        //选择起点
        m_actionStatus = (int)DrawLineStatus::Getting_StartPoint;
        StartChildAction(IPickPointAction::Create(
            PickPointExchangeData(&m_startPt, &m_isUserCancelled, &m_pickedPoints),
            nullptr));

        // 启用预设参数设置
        GbmpPresetParameterUIUtils::EnablePropertyPanel(true);
        // 更新属性面板
        GbmpPresetParameterUIUtils::UpdatePropertyPanel(GetPreSetElementId());
        opUt->Commit();
    }
}

void DrawNewLevelAction::InitAction(IUiView* pCurrentView)
{
    //选择起点
    m_actionStatus = (int)DrawLineStatus::Getting_StartPoint;
    // 显示数据输入界面。
    {
        std::vector<OwnerPtr<IControlDefinition>> dlgInputCtrlDefines;
        OwnerPtr<ICheckBoxGroupDefinition> pCheckBoxGroupDefRotation = ICheckBoxGroupDefinition::Create(s_gmWhetherCreateViewGroup, s_gmWhetherCreateViewGroup);
        CheckState bCreatePlanView = m_createAssociatedPlanView ? CheckState::Checked : CheckState::Unchecked;
        pCheckBoxGroupDefRotation->AddCheckBox(s_gmCheckCreatePlanView, GBMP_TR(L"创建对应平面视图"), GBMP_TR(L"创建对应平面视图"), bCreatePlanView);
        //CheckState bCreateCeilingView = m_createAssociatedCeilingView ? CheckState::Checked : CheckState::Unchecked;
        //pCheckBoxGroupDefRotation->AddCheckBox(s_gmCheckCreateCeilingView, GBMP_TR(L"创建对应天花板视图"), GBMP_TR(L"创建对应天花板视图"), bCreateCeilingView);
        dlgInputCtrlDefines.push_back(TransferOwnership(pCheckBoxGroupDefRotation));
        m_pInputDialog = IActionInputDialog::Create(GBMP_TR(L"标高"), TransferOwnership(dlgInputCtrlDefines), nullptr);

        // 订阅输入框消息
        if (m_pInputDialog && m_pInputDialog->GetInputEvent())
        {
            m_pInputDialog->GetInputEvent()->Add(this);
        }
    }

    // 预设参数
    {
        ElementId typeId = m_typeId;
        OwnerPtr<IUserTransaction> opUt = IUserTransaction::Create(GetDoc(), GBMP_TR(L"设置构件预设参数"));
        // 初始化预设参数
        bool bInit = StructureInstancePresetParameterUtils::SetPresetInstanceType(GetDoc(), BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION, 0, typeId);
        opUt->Commit();

        if (bInit)
        {
            ElementId tempElementId = CreateTempPreSetElement(pCurrentView, typeId);
            DBG_WARN_AND_RETURN_VOID_UNLESS(tempElementId.IsValid(), L"tempElementId为无效ID",L"GDMPLab",L"2024-03-30");

            // 更新属性面板
            GbmpPresetParameterUIUtils::UpdatePropertyPanel(tempElementId);
        }
    }

    StartChildAction(IPickPointAction::Create(
        PickPointExchangeData(&m_startPt, &m_isUserCancelled, &m_pickedPoints),
        nullptr));
}

void DrawNewLevelAction::RemoveTempLevel()
{
    if (nullptr != m_pLevelLine)
    {
        IDocument* pDoc = GetDoc();
        pDoc->DeleteElement(m_pLevelLine->GetOwnerElement()->GetElementId());
        m_pLevelLine = nullptr;
    }

}

void DrawNewLevelAction::DrawTempLevelLine(IUiView* pCurrentView, const Vector3d& pos, const gcmp::Vector3d& mousePt)
{
    if (Vector2dUtils::IsEqual(pos.Vec2d(), m_startPt.Vec2d()))
    {
        return;
    }

    IDocument* pDoc = GetDoc();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    Vector3d newTempPos(pos.X(), pos.Y(), m_startPt.Z());
    gcmp::IModelView* pModelView = pCurrentView->GetModelView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    Vector3d viewDir = pModelView->GetViewDirection();

    IGenericElement* pGenericElement = quick_cast<IGenericElement>(pDoc->GetElement(GetPreSetElementId()));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGenericElement, L"pGenericElement为空",L"GDMPLab",L"2024-03-30");

    // 获取预设参数的值（名字和标头类型）
    std::wstring nameWstring = pGenericElement->GetBasicInformation()->GetName();
    std::vector<OwnerPtr<IParameter>> headerParams = GetLevelHeaderBehaviorParameters(pGenericElement);

    // 删除临时的标高
    RemoveTempLevel();

    m_pLevelLine = ILevel::Create(pDoc, m_typeId, nameWstring, m_startPt, newTempPos, viewDir, ElementId::InvalidID, ElementCreationOptions::Transient);
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_pLevelLine, L"创建Level失败",L"GDMPLab",L"2024-03-30");

    // 更新预设参数的值（标头类型）
    UpdateLevelHeaderBehaviorParameters(m_pLevelLine, headerParams);

    GbmpPresetParameterUIUtils::UpdatePropertyPanel(m_pLevelLine->GetOwnerElement()->GetElementId());
    GbmpPresetParameterUIUtils::EnablePropertyPanel(false);

    UpdateView();
}

void DrawNewLevelAction::On(IActionInputEventArgs* pArgs)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs为空",L"GDMPLab",L"2024-03-30");
    const std::pair<std::wstring, gcmp::Any>& msg = pArgs->GetInputMessage();
    const std::wstring eventID = msg.first;
    gcmp::Any inputValue = msg.second;
    if (eventID == s_gmWhetherCreateViewGroup + L"_"+ s_gmCheckCreatePlanView)
    {
        m_createAssociatedPlanView = inputValue.cast<bool>();
    }
    if (eventID == s_gmWhetherCreateViewGroup + L"_" + s_gmCheckCreateCeilingView)
    {
        m_createAssociatedCeilingView = inputValue.cast<bool>();
    }
}

gcmp::ElementId DrawNewLevelAction::CreateTempPreSetElement(IUiView* pCurrentView, gcmp::ElementId levelTypeId)
{
    IDocument* pDoc = GetDoc();

    gcmp::Vector3d startPt(0, 0, 0);
    gcmp::Vector3d endPt(1, 0, 0);

    OwnerPtr<IUserTransaction> opUt = IUserTransaction::Create(pDoc, GBMP_TR(L"创建GBMP标高实例"));
    Vector3d upViewDir(0, 1, 0);

    std::wstring nameWstring = GetRawLevelName(pDoc);
    ILevel* pLevel = ILevel::Create(pDoc, m_typeId, nameWstring, startPt, endPt, upViewDir, ElementId::InvalidID, ElementCreationOptions::Transient);
    DBG_WARN_AND_RETURN_UNLESS(pLevel, ElementId::InvalidID, L"level为空",L"GDMPLab",L"2024-03-30");

    IElement* pElement = pLevel->GetOwnerElement();
    DBG_WARN_AND_RETURN_UNLESS(pElement, ElementId::InvalidID, L"pElemment为空",L"GDMPLab",L"2024-03-30");
    IElementStatus* pStatus = pElement->GetStatus();
    DBG_WARN_AND_RETURN_UNLESS(pStatus, ElementId::InvalidID, L"无法获取状态集",L"GDMPLab",L"2024-03-30");
    pStatus->SetIsVisible(false);
    opUt->Commit();

    ElementId elementId = pElement->GetElementId();
    SetPreSetElementId(elementId);
    return elementId;
}
